//
// Created by changlei on 2022/4/2.
//

import Foundation


/// 处理自定义高
protocol UICollectionViewWaterfallLayoutDelegate: NSObjectProtocol {
    func collectionView(_ collectionView: UICollectionView, heightForItemAt indexPath: IndexPath) -> CGFloat

    func numberOfColumns(_ collectionView: UICollectionView) -> Int
}

/// 瀑布流布局
class UICollectionViewWaterfallLayout: UICollectionViewLayout {
    var minimumLineSpacing: CGFloat = 0
    var minimumInteritemSpacing: CGFloat = 0

    // 代理 用来获取元素的高度
    weak var delegate: UICollectionViewWaterfallLayoutDelegate?

    // 缓存，将计算好的 UICollectionViewLayoutAttributes 存储起来，防止重复计算
    private var cachedAttributes = [UICollectionViewLayoutAttributes]()
    // 内容高度，为collectionViewContentSize 准备
    private var contentHeight: CGFloat = 0
    // 宽度 一个计算属性
    private var width: CGFloat {
        guard let collectionView = collectionView else {
            return 0.0
        }
        let bounds = collectionView.bounds
        let contentInset = collectionView.contentInset
        let safeInsets = collectionView.safeAreaInsets
        return bounds.width - contentInset.left - contentInset.right - safeInsets.left - safeInsets.right
    }

    override func prepare() {
        super.prepare()
        if let collectionView = collectionView, let delegate = delegate {
            resolve(collectionView: collectionView, delegate: delegate)
        }
    }

    override var collectionViewContentSize: CGSize {
        CGSize(width: width, height: contentHeight)
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttributes = [UICollectionViewLayoutAttributes]()
        for item in cachedAttributes {
            if item.frame.intersects(rect) {
                layoutAttributes.append(item)
            }
        }
        return layoutAttributes
    }

    private func resolve(collectionView: UICollectionView, delegate: UICollectionViewWaterfallLayoutDelegate) {
        let numberOfColumns = delegate.numberOfColumns(collectionView)
        cachedAttributes.removeAll()
        contentHeight = 0
        let safeAreaLeft = collectionView.safeAreaInsets.left
        let columnWidth = (width - (CGFloat(numberOfColumns) + 1) * minimumInteritemSpacing) / CGFloat(numberOfColumns)
        var xOffsets = [CGFloat]()
        for column in 0..<numberOfColumns {
            xOffsets.append(CGFloat(column) * columnWidth + minimumInteritemSpacing * CGFloat(column + 1) + safeAreaLeft)
        }
        var yOffsets = [CGFloat](repeating: minimumInteritemSpacing, count: numberOfColumns)
        var column = 0
        for item in 0..<collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            let height = delegate.collectionView(collectionView, heightForItemAt: indexPath)
            let frame = CGRect(x: xOffsets[column], y: yOffsets[column], width: columnWidth, height: height)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = frame
            cachedAttributes.append(attributes)
            contentHeight = max(contentHeight, frame.maxY)
            yOffsets[column] = yOffsets[column] + height + minimumInteritemSpacing
            column = column >= (numberOfColumns - 1) ? 0 : column + 1
        }
    }
}